package de.hdu.mfz.ctrl;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.Map;
import java.util.Random;

import javax.servlet.http.HttpSession;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.AnnotationConfiguration;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import de.hdu.mfz.dao.RouteDAO;
import de.hdu.mfz.dao.UserDAO;
import de.hdu.mfz.dto.AJAXMessage;
import de.hdu.mfz.dto.RouteZeile;
import de.hdu.mfz.dto.RouteZeile_Ort;
import de.hdu.mfz.model.Route;
import de.hdu.mfz.model.Routenpunkt;
import de.hdu.mfz.model.User;

//TODO Auf zwei Controller aufteilen: Route und Login + ein neuer Controller: User

@Controller
public class MarkusTestController {
	
	/** Standardkonstruktor */
	public MarkusTestController() {
		
	}

	/** Suche nach Fahrten (nur noch nicht beendetet)
	 * 
	 * @param von Abfahrtsort (String)
	 * @param nach Ankunftsort (String)
	 * @param tag Abfahrtsdaum und Uhrzeit (tt.mm.jjjj hh:mm)
	 * @param session (HTTPSession, Wird von Swing übergeben)
	 * @return Objekt vom Typ AJAXMessages. AJAXMessages.returnMessage enthält eine Array von RouteZeile Objekten
	 */
	@RequestMapping(value="/sucheRoute.json", method=RequestMethod.GET)
	public @ResponseBody AJAXMessage searchRoute(String von, String nach, String tag, HttpSession session) {
		Date abfahrt;
		
		if (von == "") {
			// TODO Ggf. Fehlermeldungen übersetzten in mehrere Sprachen
			return new AJAXMessage(false, AJAXMessage.FailureCode.WarningMessage, "Bitte geben Sie Ihren Abfahrtsort an.");	
		}
		if (nach == "") {
			return new AJAXMessage(false, AJAXMessage.FailureCode.WarningMessage, "Bitte geben Sie Ihren Zielort an.");	
		}
		if (tag == "") {
			return new AJAXMessage(false, AJAXMessage.FailureCode.WarningMessage, "Bitte gegen Sie ein Abfahrtsdatum an.");	
		}
		
		try {
			abfahrt = this.checkAndConvertDate(tag,null);
		} catch (Exception e) {
			return new AJAXMessage(false, AJAXMessage.FailureCode.WarningMessage, "Bitte geben Sie ein korrektes Datum ein! (tt.mm.jjjj)");
		}

		//ArrayList für Ergebnis. ArrayList hat den Vorteil, dass nach FIFO sortiert wird.
		ArrayList<RouteZeile> retListe = new ArrayList<RouteZeile>();
		
		//TODO: Hibernate Session Management gehört hier raus --> HibernateTemplate
		SessionFactory sf = new AnnotationConfiguration().configure().buildSessionFactory();
		Session DBsession = sf.getCurrentSession();
		DBsession.beginTransaction();
		
		RouteDAO dao = new RouteDAO(DBsession);
		// TDDO Datum prüfen
		List<Route> routen = dao.findRoute(von, nach);
		
		//Für jede Route ein RouteZeile-Objekt erzeugen und in Ergebnis-Array speichern.
		for (Route r : routen)
			retListe.add(new RouteZeile(r, von, nach));
		
	    return new AJAXMessage(true, AJAXMessage.FailureCode.None, retListe.toArray());
	}

	/** Liest eine Route anhand der ID und gibt ein RouteZeile Objekt zurück
	 * 
	 * @param id Eindeutige ID der Route (aus der Datenbank)
	 * @param session (HTTPSession, Wird von Swing übergeben) 
	 * @return Objekt vom Typ AJAXMessages. AJAXMessages.returnMessage enthält ein RouteZeile Objekt
	 */
	@RequestMapping(value="/getRoute.json", method=RequestMethod.GET)
	public @ResponseBody AJAXMessage getRoute(int id, HttpSession session) {
		//TODO: Hibernate Session Management gehört hier raus --> HibernateTemplate
		SessionFactory sf = new AnnotationConfiguration().configure().buildSessionFactory();
		Session DBsession = sf.getCurrentSession();
		DBsession.beginTransaction();
		
		RouteDAO Rdao = new RouteDAO(DBsession);
		try {
			Route r = Rdao.findById(id);
			if (r == null)
				throw new Exception("Nicht gefunden");
			
			return new AJAXMessage(true, AJAXMessage.FailureCode.None, new RouteZeile(r, "", ""));
		} catch (Exception e) { 
			return new AJAXMessage(false, AJAXMessage.FailureCode.WarningMessage, "Fahrt wurde leider zwischenzeitlich gelöscht.");
		}
	}
	
	/** Aktuell angemeldeten User als Mitfahrer in eine Fahrt eintragen.
	 * 
	 * @param id Eindeutige ID der Route
	 * @param session (HTTPSession, Wird von Swing übergeben) 
	 * @return Objekt vom Typ AJAXMessages. AJAXMessages.returnMessage enthält eine Nachricht an den Anwender.
	 */
	@RequestMapping(value="/joinRoute.json", method=RequestMethod.GET)
	public @ResponseBody AJAXMessage joinRoute(int id, HttpSession session) {

		//Für diese Funktion ist ein Login erforderlich
		User myUser;
		try {
			myUser = this.getUserFromSession(session);
		} catch (Exception e) {
			return new AJAXMessage(false, AJAXMessage.FailureCode.LoginRequired, e.getMessage());
		}
		
		// TODO Sinnvoller Code. Jetzt nur Zufallsgenerator zum Testen
		Random randomGenerator = new Random();
		switch (randomGenerator.nextInt(3)) {
			case 0:
				return new AJAXMessage(false, AJAXMessage.FailureCode.InfoMessage, "Gratulation. Sie sind Mitfahrer.");
			case 1:
				return new AJAXMessage(false, AJAXMessage.FailureCode.WarningMessage, "Route wurde entweder zwischenzeitlich gelöscht oder ist voll.");
			default: 
				return new AJAXMessage(false, AJAXMessage.FailureCode.ErrorMessage, "Schwerwiegender Fehler.");
		}
		
		
	}
	
	/** Eine Fahrt in der Datenbank speichern. Prüfung der Eingabefelder wird durchgeführt.
	 * Pflichtfelder:
	 * <ul>
	 * 	<li>abdatum</li>
	 * 	<li>abzeit</li>
	 * 	<li>andatum</li>
	 * 	<li>anzeit</li>
	 * 	<li>von</li>
	 * 	<li>nach</li>
	 * 	<li>plaetze</li>
	 * </ul>
	 * Zwischenhalte beginnen mit "ueber_". Nach dem Unterstrich muss eine Nummer folgen.
	 * 
	 * @param params Liste (Map<String, String>) der Eingabefelder
	 * @param session (HTTPSession, Wird von Swing übergeben) 
	 * @return Objekt vom Typ AJAXMessages. AJAXMessages.returnMessage enthält eine Nachricht an den Anwender (ggf. Fehlermeldung)
	 */
	@RequestMapping(value="/saveRoute.json", method=RequestMethod.POST)
	public @ResponseBody AJAXMessage saveRoute(@RequestParam Map<String, String> params, HttpSession session) {

		//Für diese Funktion ist ein Login erforderlich
		User myUser;
		try {
			myUser = this.getUserFromSession(session);
		} catch (Exception e) {
			return new AJAXMessage(false, AJAXMessage.FailureCode.LoginRequired, e.getMessage());
		}	
		
		
		//  1. Formularfelder in lokale Variablen speichern 
		String par_abdatum;
		String par_abzeit;
		String par_andatum;
		String par_anzeit;
		String par_von;
		String par_nach;
		String par_plaetze;
		try {
			par_abdatum = params.get("abdatum");
			par_abzeit = params.get("abzeit");
			par_andatum = params.get("andatum");
			par_anzeit = params.get("anzeit");
			par_von = params.get("von");
			par_nach = params.get("nach");
			par_plaetze = params.get("plaetze");
		} catch (Exception e) {
			//Fehler ausgeben, wenn übergebenes Formular unvollständig ist
			return new AJAXMessage(false, AJAXMessage.FailureCode.WarningMessage, "Schwerwiegender Fehler bei der Verarbeitung Ihrer Anfrage.");
		}		

		//  2. 'Dynamische' Liste mit Zwischenhalten abrufen und sortieren
		ArrayList<RouteZeile_Ort> orteListe = new ArrayList<RouteZeile_Ort>();
		try {
			//Am Anfang Abfahrtsort hinzufügen. Id ist immer 0
			orteListe.add(new RouteZeile_Ort(0, par_von));
			int tmpid; //tmpid ist zum Zwischenspeichern der Nummer des Zwischenhalts. Ginge auch ohne tmpid. Ist aber so leichter zum debuggen
			for (Map.Entry<String,String> einParameter : params.entrySet()) {
				if (einParameter.getKey().length() >= 7
					&& einParameter.getKey().substring(0,6).equals("ueber_") //Alle Felder mit den Zwischenorten beginnen mit ueber_
					&& ! einParameter.getKey().equals("ueber_blanko")) { 
					if (!einParameter.getValue().equals("")) {
						tmpid = Integer.parseInt(einParameter.getKey().substring(6));
						orteListe.add(new RouteZeile_Ort(tmpid, einParameter.getValue()));
					}
				}
			}
			java.util.Collections.sort(orteListe);
			//Zum Schluss Zielort hinzufügen. Id ist einfach um 1 höher als die höchste ID der bestehenden Zwischenhalte
			orteListe.add(new RouteZeile_Ort(orteListe.get(orteListe.size()-1).id+1, par_nach));
			//TODO Ggf. Liste neu durchnummerieren, damit keine Lücken zwischen den einzelnen Zwischenhalten sind.
		} catch (Exception e) {
			return new AJAXMessage(false, AJAXMessage.FailureCode.WarningMessage, "Schwerwiegender Fehler bei der Verarbeitung Ihrer Anfrage.");
		}	
		
		//  3. Eingebewerte umwandeln und prüfen (Datum, Zahl, ...)
		Date abfahrt;
		Date ankunft;
		int iPlaetze;
		
		try {
			abfahrt = this.checkAndConvertDate(par_abdatum , par_abzeit);		
		} catch (Exception e) {
			return new AJAXMessage(false, AJAXMessage.FailureCode.WarningMessage, "Bitte geben Sie ein korrektes Abfahrtsdatum/-uhrzeit ein! (tt.mm.jjjj hh:mm)");
		}
		try {
			ankunft = this.checkAndConvertDate(par_andatum, par_anzeit);		
		} catch (Exception e) {
			return new AJAXMessage(false, AJAXMessage.FailureCode.WarningMessage, "Bitte geben Sie ein korrektes Ankunftsdatum/-uhrzeit ein! (tt.mm.jjjj hh:mm)");
		}
		
		if (par_von.equals("")) {
			return new AJAXMessage(false, AJAXMessage.FailureCode.WarningMessage, "Bitte geben Sie den Abfahrtsort ein!");
		}
		if (par_nach.equals("")) {
			return new AJAXMessage(false, AJAXMessage.FailureCode.WarningMessage, "Bitte geben Sie den Zielort ein!");
		}
		
		try {
			iPlaetze = Integer.parseInt(par_plaetze);
			if (iPlaetze < 1 || iPlaetze > 99) {
				throw new Exception("ungültige Plätzeanzahl");
			}
		} catch (Exception e) {
			return new AJAXMessage(false, AJAXMessage.FailureCode.WarningMessage, "Bitte geben Sie die Anzahl Ihrer freien Plätze ein! (1-99)");
		}
			
			
		/* For Debugging....
		String a = "";
		for (RouteZeile_Ort o : orteListe) {
			a = a + o.id + ": " + o.name;
		}
		return new AJAXMessage(false, AJAXMessage.FailureCode.InfoMessage, a);*/
		
		//Route speichern. ---------------------------------------------------------------
		
		//TODO: Hibernate Session Management gehört hier raus --> HibernateTemplate
		SessionFactory sf = new AnnotationConfiguration().configure().buildSessionFactory();
		Session DBsession = sf.getCurrentSession();
		DBsession.beginTransaction();
		
		RouteDAO Rdao = new RouteDAO(DBsession);
		UserDAO Udao = new UserDAO(DBsession);
		
		try {
			Route r = new Route(abfahrt, ankunft, iPlaetze, Udao.findById(myUser.getUserId()));
			for (RouteZeile_Ort o : orteListe) { //In der ArrayList <orteListe> sind Start+Zwischenhalte+Ziel (sortiert) gespeichert.
				r.getListe().add(new Routenpunkt(o.name, o.id, r)); //Aus dem dto "Ort" ein Model-Objekt RoutenPunkt erzeugen
			}
			Rdao.insert(r);
			
		} catch (Exception e) {
			return new AJAXMessage(false, AJAXMessage.FailureCode.WarningMessage, "Schwerwiegender Fehler beim Speichern der neuen Fahrt.");
		}	
		
		DBsession.getTransaction().commit();
		
		return new AJAXMessage(true, AJAXMessage.FailureCode.InfoMessage, "Ihre Fahrt wurde gespeichert.");
		
		
	}
	
	/** Anmelden  (Session erzeugen)
	 * 
	 * @param benutzername
	 * @param passwort
	 * @param session (HTTPSession, Wird von Swing übergeben)
	 * @return Objekt vom Typ AJAXMessages. AJAXMessages.returnMessage enthält ggf. eine Fehlermeldung
	 */
	@RequestMapping(value="/login.json", method=RequestMethod.POST)
	public @ResponseBody AJAXMessage login(String benutzername, String passwort, HttpSession session) {

		//TODO Hibernate Session Management gehört hier raus --> HibernateTemplate
		SessionFactory sf = new AnnotationConfiguration().configure().buildSessionFactory();
		Session DBsession = sf.getCurrentSession();
		DBsession.beginTransaction();
		UserDAO Udao = new UserDAO(DBsession);
		User foundUser=null;
		
		//TODO UserDAO.getByName ... oder so ähnlich
		for (User u : Udao.findByUsername(benutzername)) {
			if (u.getUsername().equals(benutzername)) {
				foundUser = u;
				break;
			}
		}
		
		if (foundUser != null) {
			if (foundUser.getPasswort().equals(passwort)) {
				session.setAttribute("userid", foundUser.getUserId());
				return new AJAXMessage(true, AJAXMessage.FailureCode.None, "");
			} else {
					return new AJAXMessage(false, AJAXMessage.FailureCode.LoginRequired, "Falscher Benutzername oder falsches Passwort.");	
			}
		} else { 
			return new AJAXMessage(false, AJAXMessage.FailureCode.LoginRequired, "Falscher Benutzername oder falsches Passwort.");
		}
	}	

	/** Abmelden (Session löschen)
	 * 
	 * @param session (HTTPSession, Wird von Swing übergeben)
	 * @return Objekt vom Typ AJAXMessages. 
	 */
	@RequestMapping(value="/logout.json", method=RequestMethod.GET)
	public @ResponseBody AJAXMessage logout(HttpSession session) {
		session.invalidate(); 
		return new AJAXMessage(true, AJAXMessage.FailureCode.None, "");
	}

	/** Prüfen, ob User aktuell angemeldet ist und ob Session noch gültig ist.
	 * 
	 * @param session (HTTPSession, Wird von Swing übergeben)
	 * @return Objekt vom Typ AJAXMessages. AJAXMessages.returnMessage enthält ggf. eine Fehlermeldung, wenn User nicht angemeldet oder Session nicht mehr gültig ist.
	 */
	@RequestMapping(value="/checkSession.json", method=RequestMethod.GET)
	public @ResponseBody AJAXMessage checkSession(HttpSession session) {
		/*Wenn getUserFromSession keine Exception produziert, dann ist die Sitzung gültig
		 *und die Funktion gibt true zurück. Ansonsten wird false und der Grund zurückgegeben
		 *warum die Session nicht mehr gültig ist (=Fehlermeldung)*/
		try {
			getUserFromSession(session);
		} catch (Exception e) {
			return new AJAXMessage(false, AJAXMessage.FailureCode.None, e.getMessage());
		}
		return new AJAXMessage(true, AJAXMessage.FailureCode.None, "");
	}
	
	
	/**Hilfsfunktion zum Umwandeln eines Strings in ein Date-Objekt
	 * Gültige Datumsformate:
	 * strDatum       strZeit
	 * ------------   -------
	 * - tt.mm.jjjj   hh:mm    
	 * - tt.mm.jj 	  hh:mm
	 * - tt.mm.jjjj
	 * - tt.mm.jj 
	 * Das kleinste, akzeptierte Datum ist der 01.01.2000
	 * 
	 * @param Datum als String
	 * @return Date-Objekt 
	 * @throws Exception: Falls Datum im falschen Format oder ungültig
	 */
	private Date checkAndConvertDate(String strDatum, String strZeit) throws Exception {
		Date tmpDate;
		GregorianCalendar cal = new GregorianCalendar();
		//HowTo String -> Date: http://stackoverflow.com/questions/999172/how-to-parse-date-in-java
		if (strZeit != null && strZeit.length() > 0) 
			tmpDate = new SimpleDateFormat("dd.mm.yyyy hh:mm").parse(strDatum + " " + strZeit);
		else
			tmpDate = new SimpleDateFormat("dd.mm.yyyy").parse(strDatum);
		
		/* 1. String in Datum umwandeln. 
		 * 2. Datum prüfen. Dazu braucht man in Java ein Calendar Objekt. Anders kann man mit einem Datum nicht rechnen
		 * 3. Datum prüfen auf zweistellig (wenn ja, dann + 2000 Jahre)
		 * 4. Datum zwischen 100 und 2000 wird nicht akzeptiert. Ist sicher ein Tippfehler*/
		
		cal.setTime(tmpDate);
		//Falls jemand 12 anstatt von 2012 eintippt, dann muss man 2000 Jahre addieren.
		if (cal.get(Calendar.YEAR) > 0 && cal.get(Calendar.YEAR) < 100)
			cal.add(Calendar.YEAR, 2000);
		if (cal.get(Calendar.YEAR) > 100 && cal.get(Calendar.YEAR) < 2000)
			throw new Exception("Datum ungültig");
		
		return cal.getTime();
	}

	/**Hilfsfunktion zum Prüfen ob eine Session gültig ist
	 * 
	 * @param session (HTTPSession, Wird von Swing übergeben)
	 * @return User Objekt
	 * @throws Exception Wenn Session nicht mehr gültig ist.
	 */
	private User getUserFromSession(HttpSession session) throws Exception {
		if (session.getAttribute("userid") == null) {
			throw new Exception(""); //Benutzer ist nicht angemeldet, Session ist ungültig
		}
		int uid = (Integer)session.getAttribute("userid");
		
		
		//TODO Hibernate Session Management gehört hier raus --> HibernateTemplate
		SessionFactory sf = new AnnotationConfiguration().configure().buildSessionFactory();
		Session DBsession = sf.getCurrentSession();
		DBsession.beginTransaction();
		UserDAO Udao = new UserDAO(DBsession);
		User foundUser=null;
		
		//Dann prüfen, ob es user gibt
		foundUser = Udao.findById(uid) ;
		if (foundUser == null)
			throw new Exception("Ihre Zugangsdaten sind nicht mehr gültig.");
		
		//TODO Evtl. ncoh prüfen, ob Session abgelaufen ist
		//throw new Exception("Ihre Sitzung ist abgelaufen. Bitte melden Sie sich erneut an.");
		
		return foundUser;
	}
}
